package com.ruoyi.framework.config

import org.apache.commons.lang3.StringUtils
import org.apache.ibatis.io.VFS
import org.apache.ibatis.session.SqlSessionFactory
import org.mybatis.spring.SqlSessionFactoryBean
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.core.io.Resource
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.core.io.support.ResourcePatternResolver
import org.springframework.core.type.classreading.CachingMetadataReaderFactory
import org.springframework.core.type.classreading.MetadataReader
import org.springframework.core.type.classreading.MetadataReaderFactory
import org.springframework.util.ClassUtils
import java.io.IOException
import java.util.*
import javax.sql.DataSource

/**
 * Mybatis支持*匹配扫描包
 *
 * @author ruoyi
 */
@Configuration
open class MyBatisConfig {
    @Autowired
    private lateinit var env: Environment
    private fun resolveMapperLocations(mapperLocations: Array<String?>?): Array<Resource> {
        val resourceResolver: ResourcePatternResolver = PathMatchingResourcePatternResolver()
        val resources: MutableList<Resource> = mutableListOf()
        if (mapperLocations != null) {
            for (mapperLocation in mapperLocations) {
                try {
                    val mappers = resourceResolver.getResources(mapperLocation!!)
                    resources.addAll(mutableListOf(*mappers))
                } catch (e: IOException) {
                    // ignore
                }
            }
        }
        return resources.toTypedArray()
    }

    @Bean
    @Throws(Exception::class)
    open fun sqlSessionFactory(dataSource: DataSource?): SqlSessionFactory? {
        var typeAliasesPackage = env!!.getProperty("mybatis.typeAliasesPackage")
        val mapperLocations = env.getProperty("mybatis.mapperLocations")
        val configLocation = env.getProperty("mybatis.configLocation")
        typeAliasesPackage = typeAliasesPackage?.let { setTypeAliasesPackage(it) }
        VFS.addImplClass(SpringBootVFS::class.java)
        val sessionFactory = SqlSessionFactoryBean()
        sessionFactory.setDataSource(dataSource)
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage)
        sessionFactory.setMapperLocations(*resolveMapperLocations(StringUtils.split(mapperLocations, ",")))
        sessionFactory.setConfigLocation(configLocation?.let { DefaultResourceLoader().getResource(it) })
        return sessionFactory.getObject()
    }

    companion object {
        private const val DEFAULT_RESOURCE_PATTERN = "**/*.class"
        fun setTypeAliasesPackage(typeAliasesPackage: String): String {
            var newTypeAliasesPackage = typeAliasesPackage
            val resolver = PathMatchingResourcePatternResolver() as ResourcePatternResolver
            val metadataReaderFactory: MetadataReaderFactory = CachingMetadataReaderFactory(resolver)
            val allResult: MutableList<String> = mutableListOf()
            try {
                for (aliasesPackage in newTypeAliasesPackage.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
                    val result: MutableList<String> = mutableListOf()
                    val newAliasesPackage = (ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                            + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim { it <= ' ' }) + "/" + DEFAULT_RESOURCE_PATTERN)
                    val resources = resolver.getResources(newAliasesPackage)
                    if (resources.isNotEmpty()) {
                        var metadataReader: MetadataReader?
                        for (resource in resources) {
                            if (resource.isReadable) {
                                metadataReader = metadataReaderFactory.getMetadataReader(resource)
                                try {
                                    result.add(Class.forName(metadataReader.classMetadata.className).getPackage().name)
                                } catch (e: ClassNotFoundException) {
                                    e.printStackTrace()
                                }
                            }
                        }
                    }
                    if (result.size > 0) {
                        val hashResult = HashSet(result)
                        allResult.addAll(hashResult)
                    }
                }
                newTypeAliasesPackage = if (allResult.size > 0) {
                    java.lang.String.join(",", *allResult.toTypedArray())
                } else {
                    throw RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包")
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
            return newTypeAliasesPackage
        }
    }
}
